#!/usr/bin/env python
#
#       Interfaces.py
#       
#       Copyright 2009 Ben Davis <its.hip@gmail.com>, Pete Lord <mr.petelord@gmail.com>, Robin Hayes <bonzairob@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.





#CURRENT BUGS:
#if the server crashes, adn you connect to another server, you're suck with a join() on top of the game after lobby.



import os
import sys
import pygame
import string
import subprocess
import threading
import time

import Events
import Options
import ToServer

from Client import Client
client = Client()

pygame.font.init()

LEFT = TOP = 0
CENTRE = CENTER = 1
RIGHT = BOTTOM = 2

class Vertical(list):
    def __init__(self, parent, background=(0, 0, 0, 0), align=LEFT, padding=5):
        list.__init__(self)
        self.parent = parent
        self.parent.append(self)
        self.background = background
        self.rect = pygame.Rect(0, 0, 0, 0)
        self.align = align
        self.padding = padding
        self.redraw()
        self.name=""
        
    def die(self):
        for child in self[:]:
            self.remove(child)
    
    def remove(self, x):
        x.die()
        list.remove(self, x)
        
    def update(self):
        for widget in self:
            widget.update()
    
    def draw(self):
#        print "Drawing", self.__class__.__name__, "at:", self.rect
        return self.surface, self.rect
        
    def redraw(self, *args):
        # Initialise a new rect
        self.rect.size = (self.padding, self.padding)
        # Calculate our rect width and height
        for widget in self:
            surface, position = widget.draw()
            if position.width + self.padding * 2 > self.rect.width:
                self.rect.width = position.width + self.padding * 2
            self.rect.height += position.height + self.padding
        # Create a new surface for the container
        self.surface = pygame.Surface(self.rect.size).convert_alpha()
        self.surface.fill(self.background)
        # Keep track of the current x position for the next widget
        current = self.padding
        # Define where widgets need to go
        for widget in self:
            widget.rect.top = current
            if self.align == LEFT:
                widget.rect.left = self.padding
            if self.align == CENTRE:
                widget.rect.centerx = self.rect.width / 2
            if self.align == RIGHT:
                widget.rect.right = self.rect.width - self.padding
            surface, position = widget.draw()
            self.surface.blit(surface, position)
            current += position.height + self.padding
        self.parent.redraw()
        
        
    def over(self, pos):
        x, y = pos
        x -= self.rect.x
        y -= self.rect.y
        pos = x, y
        for widget in self:
            over, local_pos = widget.over(pos)
            if over is not None:
                return over, local_pos
        return None, None
        
    def focus(self):
        return
    
    def unfocus(self):
        return
        
    def click(self, pos):
        return
    
    def clicked(self, pos):
        """Called on a successful mousebuttondown, mousebuttonup combo"""
        return
        
    def drag(self, pos):
        """Called when mousebuttondown has happened on this widget followed by
        a mousemotion"""
        return
    
    def dragged(self, pos):
        """Called on a successful mousebuttondown, mousemotion, mousebuttonup
        combo"""
        return
        
    def hover(self, pos):
        """Run when the widget is hovered over"""
        return
        
    def unhover(self):
        return
        
class Horizontal(Vertical):
    def redraw(self, *args):
        # Initialise a new rect
        self.rect.size = (self.padding, self.padding)
        # Calculate our rect width and height
        for widget in self:
            surface, position = widget.draw()
            if position.height + self.padding * 2 > self.rect.height:
                self.rect.height = position.height + self.padding * 2
            self.rect.width += position.width + self.padding
        # Create a new surface for the container
        self.surface = pygame.Surface(self.rect.size).convert_alpha()
        self.surface.fill(self.background)
        # Keep track of the current x position for the next widget
        current = self.padding
        # Define where widgets need to go
        for widget in self:
            widget.rect.left = current
            if self.align == TOP:
                widget.rect.top = self.padding
            if self.align == CENTRE:
                widget.rect.centery = self.rect.height / 2
            if self.align == BOTTOM:
                widget.rect.bottom = self.rect.height - self.padding
            surface, position = widget.draw()
            self.surface.blit(surface, position)
            current += position.width + self.padding
        self.parent.redraw()
        
class Absolutes(Vertical):
    """Container which sizes to parent element and places children by their
    specified position"""
    def redraw(self):
        # Initialise a new rect
        self.rect.size = self.parent.rect.size
        self.rect.topleft = (0, 0)
        # Calculate our rect width and height
        for widget in self:
            surface, position = widget.draw()
            if position.right > self.rect.width:
                self.rect.width = position.right
            if position.bottom > self.rect.height:
                self.rect.height = position.bottom
        # Create a new surface for the container
        self.surface = pygame.Surface(self.rect.size).convert_alpha()
        self.surface.fill(self.background)
        for widget in self:
            surface, position = widget.draw()
            self.surface.blit(surface, position)
        self.parent.redraw()
        
class Screen(Vertical):
    """Container which sizes to parent element and centres its children, drawing
    one on top of the other"""
    def redraw(self):
        # Initialise a new rect
        self.rect.size = self.parent.rect.size
        self.rect.topleft = (0, 0)
        # Create a new surface for the container
        self.surface = pygame.Surface(self.rect.size).convert_alpha()
        self.surface.fill(self.background)
        for widget in self:
            surface, position = widget.draw()
            # Centre the widget
            widget.rect.center = self.rect.center
            self.surface.blit(surface, position)
        self.parent.redraw()
        

class ShipInfoContainer(Horizontal):
    def __init__(self, parent, ship):
        Horizontal.__init__(self, parent, (0, 0, 0, 100), align=CENTRE)
        self.ship = ship
        ShipInfo(self, ship)
        if ship == client.game.selected:
            if ship.player == client.game.players.turn:
                if ship.player == client.game.player.key():
                    Vertical(self)
                    # Add new Weapon widgets for the currently selected ship
                    Weapon(self[-1], client.graphics, ship, 0, client.server)
                    Weapon(self[-1], client.graphics, ship, 1, client.server)
        self.update()
        self.redraw()
    
    def update(self):
        x, y = self.ship.screen_location()
        self.rect.topleft = (x - (self.rect.width - Options.TILE_WIDTH[Options.ZOOM_LEVEL]) / 2, y - self.rect.height)
        Horizontal.update(self)

class MenuContainer(Screen):
    def __init__(self, parent):
        Screen.__init__(self, parent, (0, 0, 0, 100))
        SplashBG(self)
        Absolutes(self)
        PandRButton(self[-1])
        self.events = Events.Events()
        self.main()
        self.name = "screen"
        
    def hover(self, pos):
        return False
    
    def over(self, pos):
        x, y = pos
        x -= self.rect.x
        y -= self.rect.y
        pos = x, y
        for widget in reversed(self):
            over, local_pos = widget.over(pos)
            if over is not None:
                return over, local_pos
        return self, pos
        
    def close(self, *args):
        self.parent.remove(self)
        return True
    
    def main(self, *args):
        self.events.unbind(type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        try:
            self.remove(self.child)
        except AttributeError:
            pass
        self.child = Vertical(self, align=CENTRE, padding=40)
        HostButton(self.child)
        JoinButton(self.child)
        OptionsButton(self.child)
        QuitButton(self.child)
        client.focus(self)
        if client.game is not None:
            self.events.bind(self.close, type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        return True
        
    def join(self, *args):
        self.events.unbind(type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        self.remove(self.child)
        self.child = JoinDialog(self)
        self.events.bind(self.main, type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        for x in self.parent:
            if x.name == "screen":
                for y in x:
                    try:
                        if y.name == "bob":
                            x.remove(y)
                    except AttributeError:
                        pass
        self.redraw()

        return True
    
    def host(self, *args):
        self.events.unbind(type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        self.remove(self.child)
        self.child = Vertical(self, background=(0, 0, 0, 100), align=CENTRE)
        Text(self.child, "Notes")
        TextInput(self.child, "Anyone can join!", 100)
        Spacer(self.child, (1, 10))
        Text(self.child, "Port")
        TextInput(self.child, "7059", 100)
        StartServer(self.child)
        self.events.bind(self.main, type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        client.focus(self.child[1])
        self.child[1].esc = self.main
        self.child[4].esc = self.main
        return True
        
    def options(self, *args):
        self.events.unbind(type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        self.remove(self.child)
        self.child = Vertical(self, background=(0, 0, 0, 100), align=CENTRE)
        Text(self.child, "Music Volume")
        Slider(self.child, length=200, value=client.sounds.musvol, function=client.sounds.set_music_volume)
        Spacer(self.child, (1, 10))
        Text(self.child, "Sound Effects Volume")
        Slider(self.child, length=200, value=client.sounds.sfxvol, function=client.sounds.set_sfx_volume)
        self.events.bind(self.main, type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        
    def pandrlogin(self, *args):
        self.events.unbind(type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        self.remove(self.child)
        self.child = Vertical(self, background=(0, 0, 0, 100), align=CENTRE)
        
        Text(self.child, "Username:")
        TextInputReturn(self.child, None, "username", 100)#Return blanks, -Return resets :/
        Spacer(self.child, (1, 10))
        
        Text(self.child, "Password:")
        TextInputReturnpwd(self.child, None, "", 100)
        
        Horizontal(self.child, background=(0, 0, 0, 0), align=CENTRE)
        Text(self.child[-1], "Remember?")
        Checkbox(self.child[-1], True)
        PandRLogin(self.child)
        self.events.bind(self.main, type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        
        client.focus(self.child[1])
        self.child[1].esc = self.main
        self.child[4].esc = self.main
        
    
    def joinserver(self, *args):
        self.events.unbind(type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
        self.remove(self.child)
        self.child = Vertical(self, background=(0, 0, 0, 0), align=CENTRE)
        Horizontal(self.child, background=(0, 0, 0, 0), align=CENTRE)
        BePlayerOne(self.child[-1])
        BePlayerNone(self.child[-1])
        BePlayerTwo(self.child[-1])
        Horizontal(self.child, background=(0, 0, 0, 0), align=CENTRE)
        TextChatContainer(self.child[-1], (200,100))
        self.child[-1].esc = self
        Spacer(self.child[-1], (200, 10))
        ClientList(self.child[-1])
        self.events.bind(self.main, type=pygame.KEYDOWN, key=pygame.K_ESCAPE)
            
class Table(Vertical):
    def __init__(self, parent, columns, **kwargs):
        self.columns = [0 for column in range(columns)]
        Vertical.__init__(self, parent, **kwargs)
        
    def redraw(self):
        # Reset all sizes to 0
        self.columns = [0 for column in range(len(self.columns))]
        # For each child
        for widget in self:
            # Ask them to calculate sizes (eventually change to widget.redraw())
            columns = widget.draw()
            # For each column
            for column in range(len(self.columns)):
                # Get the width of the widget's item in the column
                width = columns[column].get_width()
                # If widget's column's width is bigger than our column's width
                if width > self.columns[column]:
                    # Grab it
                    self.columns[column] = width
        # Calculate total width of self
        self.rect.width = self.padding
        for column in self.columns:
            self.rect.width += column + self.padding
        self.rect.height = self.padding
        for widget in self:
            # Set children's width to our width
            widget.rect.width = self.rect.width
            # Increment our height
            self.rect.height += widget.rect.height + self.padding
        self.surface = pygame.Surface(self.rect.size).convert_alpha()
        self.surface.fill(self.background)
        y = self.padding
        for widget in self:
            x = self.padding
            widget.rect.topleft = (x, y)
            # Draw each column in the right place
            columns = widget.draw()
            for column in range(len(self.columns)):
                self.surface.blit(columns[column], (x, y))
                x += self.columns[column] + self.padding
            y += widget.rect.height + self.padding
        self.parent.redraw()
        
class JoinDialog(Vertical):
    def __init__(self, parent):
        Vertical.__init__(self, parent, background=(0, 0, 0, 0), align=CENTRE, padding=20)
        ServerInput(self)
        ServerList(self)
        Refresh(self, self[1])
        
class ServerInput(Vertical):
    def __init__(self, parent):
        Vertical.__init__(self, parent, background=(0, 0, 0, 100), align=CENTRE, padding=5)
        Text(self, "Type the IP or host you wish to join")
        TextInputReturn(self, self.connect, "", 100)
        client.focus(self[1])
        self[1].esc = self.parent.parent.main
        
    def connect(self, text):
        # Extract host/port from text
        if text.find(' ') != -1:
            host, port = text.split(' ')[:2]
            try:
                port = int(port)
            except ValueError:
                host, port = text, 7059
        else:
            host, port = text, 7059
        client.join(host, port)
    
    def over(self, pos):
        over, local_pos = Vertical.over(self, pos)
        if over:
            return over, local_pos
        if self.rect.collidepoint(pos):
            client.focus(self[1])
        return None, None
        
class ServerList(Table):
    def __init__(self, parent):
        Table.__init__(self, parent, 4, background=(0, 0, 0, 100), align=CENTRE, padding=5)
        self.refresher = ServerListRefresher(self)
        self.refresher.start()
        self.changed = False
        self.refresh()
    
    def die(self):
        self.refresher.quit = True
    
    def refresh(self):
        del self[:]
        Row(self, ("", "Refreshing server list...", "", ""))
        self.redraw()
        self.refresher.refresh = True
    
    def update(self):
        if self.changed:
            for child in self:
                child.redraw(False)
            self.redraw()
            self.changed = False
            
class ClientList(Table):
    def __init__(self, parent):
        Table.__init__(self, parent, 2, background=(0, 0, 0, 180), align=CENTRE, padding=5)
        #self.parent.append(Refresh(self.parent, self))
        self.refresher = ClientListRefresher(self)
        self.refresher.start()
        self.changed = False
        self.refresh()
    
    def die(self):
        self.refresher.quit = True
    
    def refresh(self):
        del self[:]
        Row(self, ("Refreshing...", ""))
        self.redraw()
        self.refresher.refresh = True
    
    def update(self):
        if self.changed:
            for child in self:
                child.redraw(False)
            self.redraw()
            self.changed = False

class ClientListRefresher(threading.Thread):
    def __init__(self, parent):
        threading.Thread.__init__(self)
        self.parent = parent
        self.quit = False
        self.refresh = True
        
    def run(self):
        while not self.quit:
            if self.parent in self.parent.parent:
                if self.refresh:
                    players = client.game.clientarray
                    del self.parent[:]
                    #if players != []:
                    Row(self.parent, ("Name", "Playing As"), False)
                    for player in client.game.clientarray:
                        PlayerRow(self.parent, player)
                    #else:
                    #    Row(self.parent, ("None?", ":/"), False)
                    self.refresh = False
                    self.parent.changed = True
                
                time.sleep(0.1)
            else:
                self.quit = True

class ServerListRefresher(threading.Thread):
    def __init__(self, parent):
        threading.Thread.__init__(self)
        self.parent = parent
        self.quit = False
        self.refresh = True
        
    def run(self):
        while not self.quit:
            if self.parent in self.parent.parent:
                if self.refresh:
                    servers = client.database.server_list()
                    del self.parent[:]
                    if servers:
                        Row(self.parent, ("Notes", "Players", "Map", "Address"), False)
                        for server in client.database.server_list():
                            ServerButton(self.parent, server)
                    else:
                        Row(self.parent, ("", "Could not find any servers online", "", ""), False)
                    self.refresh = False
                    self.parent.changed = True
                try:
                    time.sleep(0.1)
                except AttributeError:
                    pass
            else:
                self.quit = True












class Widget(object):
    """Baseclass for widgets"""
    
    font = pygame.font.Font(pygame.font.match_font('georgia'), 14)
    if (sys.platform == "darwin"):
        font = pygame.font.Font("/Library/Fonts/Georgia.ttf", 14)
    
    def __init__(self, parent, rect):
        self.parent = parent
        self.parent.append(self)
        self.rect = rect
        self.name = ""
            
    def die(self):
        pass
    
    def over(self, pos):
        x, y = pos
        if self.rect.left < 0:
            x -= self.parent.rect.left - self.rect.width
        if self.rect.top < 0:
            y -= self.parent.rect.top - self.rect.height
        if self.rect.collidepoint((x, y)):
            x -= self.rect.left
            y -= self.rect.top
            return self, (x, y)
        return None, None
    
    def click(self, pos):
        """Widget.click should return True if it doesn't grab focus but
        self if it does"""
        return
    
    def clicked(self, pos):
        """Called on a successful mousebuttondown, mousebuttonup combo"""
        return
        
    def drag(self, pos):
        """Called when mousebuttondown has happened on this widget followed by
        a mousemotion"""
        return
    
    def dragged(self, pos):
        """Called on a successful mousebuttondown, mousemotion, mousebuttonup
        combo"""
        return
        
    def draw(self):
        return self.surface, self.rect
        
    def hover(self, pos):
        """Run when the widget is hovered over"""
        return
        
    def unhover(self):
        return
        
    def focus(self):
        return
    
    def unfocus(self):
        return
    
    def update(self):
        return
        
class Text(Widget):
    """Simple widget which renders text"""
    def __init__(self, parent, text):
        self.text = text
        self.parent = parent
        self.parent.append(self)
        self.redraw()
        
    def redraw(self):
        self.surface = self.font.render(self.text, True, (255, 255, 255))
        self.rect = self.surface.get_rect()
        self.parent.redraw()

class TextInput(Text):
    history = []
    def __init__(self, parent, text="", minwidth=0):
        self.minwidth = minwidth
        self.place = 0
        self.cursor = len(text)
        self.events = Events.Events()
        self.events.bind(self.event, type=pygame.KEYDOWN)
        Text.__init__(self, parent, text)
        
        
    def redraw(self):
        if client.focused == self:
            text = self.text[:self.cursor] + "|" + self.text[self.cursor:]
        else:
            text = self.text
        surface = self.font.render(text, True, (255, 255, 255))
        width, height = surface.get_size()
        if width < self.minwidth:
            width = self.minwidth
        padding = 5
        width += padding * 2
        height += padding * 2
        self.surface = pygame.Surface((width, height)).convert_alpha()
        self.surface.fill((0, 0, 0, 0))
        pygame.draw.lines(self.surface, (255, 255, 255), False, ((padding, 0), (0, 0), (0, height - 1), (padding, height - 1)))
        pygame.draw.lines(self.surface, (255, 255, 255), False, ((width - padding - 1, 0), (width - 1, 0), (width - 1, height - 1), (width - padding - 1, height - 1)))
        self.surface.blit(surface, ((width - surface.get_width()) / 2, padding))
        self.rect = self.surface.get_rect()
        self.parent.redraw()
    
    def click(self, pos):
        return True
        
    def focus(self):
        self.redraw()
    
    def unfocus(self):
        self.redraw()
    
    def enter(self):
        return
    
    def esc(self):
        return
    
    def event(self, event):
        #if pygame.key.name(event.key) in string.printable:
        if pygame.key.name(event.key) in string.letters + string.digits + "'.,/-" or event.key == pygame.K_SPACE:
            char = chr(event.key)
            for mod in (pygame.KMOD_LSHIFT, pygame.KMOD_RSHIFT, pygame.KMOD_SHIFT, pygame.KMOD_CAPS):
                if event.mod & mod != 0:
                    char = char.upper()
                    if char in string.digits:
                        char = u')!@\u00A3$%^&*('[int(char)]
                    if char == '/':
                        char = '?'
            self.text = self.text[:self.cursor] + char + self.text[self.cursor:]
            self.cursor += 1
            self.redraw()
        elif event.key == pygame.K_BACKSPACE:
            #self.text = self.text[:-1]
            self.text = self.text[:self.cursor - 1] + self.text[self.cursor:]
            if self.cursor > 0:
                self.cursor -= 1
            self.redraw()
        elif event.key == pygame.K_RETURN:
            self.enter()
        elif event.key == pygame.K_ESCAPE:
            self.esc()
        elif event.key == pygame.K_UP:
            self.place -= 1
            try:
                self.text = self.history[self.place]
                self.cursor = len(self.text)
                self.redraw()
            except:
                self.place += 1
        elif event.key == pygame.K_DOWN:
            self.place += 1
            if self.place < 0:
                self.text = self.history[self.place]
                self.cursor = len(self.text)
                self.redraw()
            else:
                self.place = 0
                self.cursor = 0
                self.text = ""
                self.redraw()
        elif event.key == pygame.K_LEFT:
            if self.cursor > 0:
                self.cursor -= 1
                self.redraw()
        elif event.key == pygame.K_RIGHT:
            if self.cursor < len(self.text):
                self.cursor += 1
                self.redraw()
        else:
            return False
        return True

class MessageInput(TextInput):
    history = []
    def __init__(self, parent, server, minwidth=0):
        TextInput.__init__(self, parent, "", minwidth)
        self.server = server
    
    def enter(self):
        # Don't send empty messages
        if len(self.text) > 0:
            self.server.send(ToServer.Say(self.text))
            self.history.append(self.text)
        self.esc()
    
    def esc(self):
        self.parent.parent.remove(self.parent)
        self.parent.remove(self)
        
class TextInputReturn(TextInput):
    def __init__(self, parent, function, text="", minwidth=0):
        TextInput.__init__(self, parent, text, minwidth)
        self.function = function
    
    def enter(self):
        self.function(self.text)
        
class TextInputReturnpwd(TextInput):
    def __init__(self, parent, function, text="", minwidth=0):
        TextInput.__init__(self, parent, text, minwidth)
        self.function = function
    
    def enter(self):
        self.function(self.text)
        
    def redraw(self):
        obfus = ""
        for char in self.text:
            obfus += "*"
        if client.focused == self:
            text = obfus[:self.cursor] + "|" + obfus[self.cursor:]
        else:
            text = obfus
        surface = self.font.render(text, True, (255, 255, 255))
        width, height = surface.get_size()
        if width < self.minwidth:
            width = self.minwidth
        padding = 5
        width += padding * 2
        height += padding * 2
        self.surface = pygame.Surface((width, height)).convert_alpha()
        self.surface.fill((0, 0, 0, 0))
        pygame.draw.lines(self.surface, (255, 255, 255), False, ((padding, 0), (0, 0), (0, height - 1), (padding, height - 1)))
        pygame.draw.lines(self.surface, (255, 255, 255), False, ((width - padding - 1, 0), (width - 1, 0), (width - 1, height - 1), (width - padding - 1, height - 1)))
        self.surface.blit(surface, ((width - surface.get_width()) / 2, padding))
        self.rect = self.surface.get_rect()
        self.parent.redraw()

class MessageBox(Widget):
    """Simple messagebox widget which disappears when clicked"""
    def __init__(self, parent, message, place):
        self.parent = parent
        self.message = message
        self.rect = pygame.Rect(place, (0, 0))
        self.redraw()
        Widget.__init__(self, parent, pygame.Rect(place, self.surface.get_size()))
            
    def click(self, pos):
        self.parent.remove(self)

    def redraw(self):
        padding = 10
        render = self.font.render(self.message, True, (255, 255, 255))
        width, height = render.get_size()
        width += padding * 2
        height += padding * 2
        self.surface = pygame.Surface((width, height)).convert_alpha()
        self.surface.fill((0, 0, 0, 100))
        self.surface.blit(render, (padding, padding))
        self.rect.width, self.rect.height = width, height
        self.parent.redraw()
        
class MessageBoxNamed(MessageBox):
    def __init__(self, parent, message, name):
        MessageBox.__init__(self, parent, message, (-5, -5))
        self.name = name

class Tooltip(MessageBox):
    def over(self, pos):
        return None, None

class TimedBox(MessageBox):
    def __init__(self, parent, message):
        MessageBox.__init__(self, parent, message, (-5, -5))
        self.timeout = 0
        for widget in parent:
            if widget != self:
                if isinstance(widget, TimedBox):
                    widget.rect.move_ip(0, -self.rect.height - 5)
    
    def update(self):
        # If we've reached our final hour
        if self.timeout == Options.MESSAGEBOX_TIMEOUT:
            # Goodbye!
            self.parent.remove(self)
            self.parent.redraw()
        self.timeout += 1
        
class TurnBox(Widget):
    def __init__(self, parent):
        self.other_textures = []
        self.your_textures = []
        for i in range(1, 5):
            self.other_textures.append(client.graphics[os.path.join("Data", "UI", "p" + str(i) + "turn.png")])
        for i in range(1, 5):
            self.your_textures.append(client.graphics[os.path.join("Data", "UI", "p" + str(i) + "yourturn.png")])
        Widget.__init__(self, parent, pygame.Rect((5, 5), self.your_textures[0].get_size()))
        
    def clicked(self, pos):
        # End turn (next player)
        client.server.send(ToServer.EndTurn())
        return
        
    def hover(self, pos):
        """Run when the widget is hovered over"""
        return
    
    def redraw(self):
        if client.game.players.turn == client.game.player.key():
            self.surface = self.your_textures[client.game.players[client.game.players.turn].number]
        else:
            self.surface = self.other_textures[client.game.players[client.game.players.turn].number]
        self.parent.redraw()
        
class Weapon(Widget):
    def __init__(self, parent, graphics, ship, weapon, server):
        x, y = ship.shadow_location()
        self.ship = ship
        self.weapon = weapon
        self.surface = graphics[os.path.join("Data", "UI",  self.ship.weapons[self.weapon].icon +".png")]
        Widget.__init__(self, parent, pygame.Rect((0, 0), self.surface.get_size()))
        self.server = server
        self.parent.redraw()
    
    def hover(self, pos):
        """Run when the widget is hovered over"""
        # Set the tiles to draw
        client.game.targets = self.ship.weapons[self.weapon].targets
        
    def unhover(self):
        client.game.targets = []
    
    def click(self, pos):
        self.server.send(ToServer.Fire(self.ship, self.weapon))

class ShipInfo(Widget):
    """Simple widget to display info on the selected ship"""
    def __init__(self, parent, ship):
        self.ship = ship
        self.padding = 10
        self.update()
        Widget.__init__(self, parent, self.rect)

    def hovering(self, pos):
        # return None to allow click-through
        return

    def update(self):
        try:
            info = self.info()
            renders = []
            width, height = 0, 0
            for line in info:
                renders.append(self.font.render(line, True, (255, 255, 255)))
                if renders[-1].get_width() > width:
                    width = renders[-1].get_width()
                height += renders[-1].get_height()
            width += self.padding * 2
            height += self.padding * 2
            self.surface = pygame.Surface((width, height)).convert_alpha()
            self.surface.fill((0, 0, 0, 0))
            y = self.padding
            for render in renders:
                self.surface.blit(render, ((width - render.get_width()) / 2, y))
                y += render.get_height()
            self.rect = pygame.Rect(0, 0, width, height)
        except KeyError:
            self.parent.parent.remove(self.parent)
    
    def info(self):
        for player in client.game.players.values():
            if self.ship.key() in player.ships:
                if player == client.game.player:
                    name = "You"
                else:
                    name = player.name
        try: 
            return ["Class: " + str(self.ship.__class__.__name__),
                    "Player: " + name,
                    "",
                    "SU: " + str(self.ship.steam) + "/" + str(self.ship.steam_max) + "/+" + str(self.ship.steam_prod),
                    "SI: " + str(self.ship.health) + "/" + str(self.ship.health_max),
                    #"SI: " + str(int(float(self.ship.health) * 100 / self.ship.health_max)) + "%",
                   ]
        except UnboundLocalError:
            print "UNBOUNDLOCAL Interfaces 775"
            return ["Class: " + str(self.ship.__class__.__name__),
                    "Player: " + "<unknown>",
                    "",
                    "SU: " + str(self.ship.steam) + "/" + str(self.ship.steam_max) + "/+" + str(self.ship.steam_prod),
                    "SI: " + str(self.ship.health) + "/" + str(self.ship.health_max),
                    #"SI: " + str(int(float(self.ship.health) * 100 / self.ship.health_max)) + "%",
                   ]


class Compass(Widget):
    def __init__(self, parent):
        self.surface = client.graphics[os.path.join("Data", "UI", "Compass.png")]
        Widget.__init__(self, parent, pygame.Rect((-5, 5), self.surface.get_size()))

    def hovering(self, pos):
        return

class SplashBG(Widget):
    '''General gackground, used for all menus. Yes, Ratchet is the lazy.'''
    def __init__(self, parent):
        splash = client.graphics[os.path.join("Data", "UI", "splash.jpg")]
        width, height = splash.get_size()
        if client.rect.width < width:
            height = height * client.rect.width / width
            width = client.rect.width
        # Easier just to scale by width. Height later, perhaps.
        
        self.surface = pygame.transform.smoothscale(splash, (width, height))
        Widget.__init__(self, parent, pygame.Rect((0, 0), (width, height)))
        
    def click(self, pos):
        return

    def hovering(self, pos):
        return
        
class MenuButton(Widget):
    """Menu button superclass"""
    def __init__(self, parent, bitmap):
        self.surface = client.graphics[os.path.join("Data", "UI", bitmap)]
        Widget.__init__(self, parent, pygame.Rect((0, 0), self.surface.get_size()))
        self.surfaces = [(self.surface, self.surface.get_size())]
        brace = client.graphics[os.path.join("Data", "UI", "brace.png")]
        # Create a new rect to size the 'hover' surface
        hover_rect = pygame.Rect(0, 0, 0, 0)
        # Calculate the height
        if brace.get_height() > self.surface.get_height():
            hover_rect.height = brace.get_height()
        else:
            hover_rect.height = self.surface.get_height()
        # Calculate the width
        hover_rect.width = brace.get_width() * 2 + 50 + self.surface.get_width()
        # Create a new surface for the render
        hover_surface = pygame.Surface(hover_rect.size).convert_alpha()
        hover_surface.fill((0, 0, 0, 0))
        # Render the button and braces
        hover_surface.blit(brace, (0, (hover_rect.height - brace.get_height()) / 2))
        hover_surface.blit(self.surface, (25 + brace.get_width(), (hover_rect.height - self.surface.get_height()) / 2))
        hover_surface.blit(pygame.transform.flip(brace, True, False), (50 + brace.get_width() + self.surface.get_width(), (hover_rect.height - brace.get_height()) / 2))
        self.surfaces.append((hover_surface, hover_rect.size))
        self.hovered = False
        self.parent.redraw()
        
class Dismiss(Widget):
    '''GO AWAY'''
    def __init__(self, parent):
        self.surface = client.graphics[os.path.join("Data", "UI", "Ram.png")]
        Widget.__init__(self, parent, pygame.Rect((200, 200), self.surface.get_size()))
        
    def click(self, pos):
        # Get rid of menus, all of them.
        self.parent.esc()
        
    def hover(self, pos):
        # extra bit like 'close?'
        return
        
class HostButton(MenuButton):
    '''I liek to hozt plz.'''
    def __init__(self, parent):
        MenuButton.__init__(self, parent, "host.png")
        
    def click(self, pos):
        self.parent.parent.host()
        return

class QuitButton(MenuButton):
    def __init__(self, parent):
        MenuButton.__init__(self, parent, "quit.png")
    
    def click(self, pos):
        raise SystemExit
        return
        
class JoinButton(MenuButton):
    '''I liek to join plz.'''
    def __init__(self, parent):
        MenuButton.__init__(self, parent, "join.png")
        
    def click(self, pos):
        self.parent.parent.join()
        return
        
class OptionsButton(MenuButton):
    def __init__(self, parent):
        MenuButton.__init__(self, parent, "options.png")
        
    def click(self, pos):
        self.parent.parent.options()
        return
    
        
class Row(Widget):
    def __init__(self, parent, columns, redraw=True, color=(255,255,255)):
        Widget.__init__(self, parent, pygame.Rect(0, 0, 0, 0))
        self.columns = columns
        self.color = color
        if redraw:
            self.redraw()
    
    def redraw(self, parent=True):
        self.surfaces = []
        self.rect.height = 0
        for column in self.columns:
            self.surfaces.append(self.font.render(column, True, self.color))
            if self.surfaces[-1].get_height() > self.rect.height:
                self.rect.height = self.surfaces[-1].get_height()
        if parent:
            self.parent.redraw()
            
    def draw(self):
        return self.surfaces
        
class ServerButton(Row):
    def __init__(self, parent, server):
        self.server = server
        columns = [server['notes'], str(len(server['users'])), server['map'], server['address'] + ":" + server['port']]
        Row.__init__(self, parent, columns, False)
        
    def click(self, pos):
        client.join(self.server['address'], int(self.server['port']))
        
class PlayerRow(Row):
    def __init__(self, parent, player):
        columns = [str(player[0]), str(player[1])]
        if player[1] == 2:
            color = (255, 200, 200)
        elif player[1] == 1:
            color = (200,255,255)
        elif player[1] == 0:
            color = (255,255,255)
        else:
            color = (200,200,200)
            
        Row.__init__(self, parent, columns, False, color)
        

'''class ImgButton(Widget):
    def __init__(self, parent, img):
        self.surface = client.graphics[os.path.join("Data", "UI",  img)]
        Widget.__init__(self, parent, pygame.Rect((0, 0), self.surface.get_size()))
        self.parent = parent
'''
class StartServer(Widget):
    def __init__(self, parent):
        self.surface = client.graphics[os.path.join("Data", "UI",  "startserver.png")]
        Widget.__init__(self, parent, pygame.Rect((0, 0), self.surface.get_size()))
        self.parent = parent
    
    def click(self, pos):
        try:
            port = int(self.parent[4].text)
        except ValueError:
            port = 7059
            print "Port is default, 7059."
        client.server_process = subprocess.Popen(["python", os.path.join("Server", "Server.py"), str(port), self.parent[1].text])
        #Need to kill it afterwards :/
        pygame.time.wait(Options.HOSTCONNECT_TIMEOUT)
        client.join("localhost", port)
        return

class PandRLogin(Widget):
    def __init__(self, parent):
        self.surface = client.graphics[os.path.join("Data", "UI",  "login.png")]
        Widget.__init__(self, parent, pygame.Rect((0, 0), self.surface.get_size()))
        self.parent = parent
    
    
    def click(self, pos):
        #print "Sending data", "["+self.parent[1].text+"]", "["+self.parent[4].text+"]", "R?", self.parent[5][1].value
        client.database.loginbynum(self.parent, self.parent[1].text, self.parent[4].text,self.parent[5][1].value)
        #if (self.parent[5][1].value == True):
            #rememebr in Options? Write out to file?
        #Could also wait on received data, lol.
        return

class PandRButton(Widget):
    def __init__(self, parent):
        self.surface = client.graphics[os.path.join("Data", "UI",  "pandrsmall.png")]
        self.rect = pygame.Rect((0, 0), self.surface.get_size())
        Widget.__init__(self, parent, self.rect)
        self.parent = parent
    
    def click(self, pos):
        self.parent.pandrlogin()
        #self.parent.joinserver()
        return
        
class BackButton(Widget):
    def __init__(self, parent, function):
        self.surface = client.graphics[os.path.join("Data", "UI",  "back.png")]
        self.rect = pygame.Rect((0, 0), self.surface.get_size())
        Widget.__init__(self, parent, self.rect)
        self.parent = parent
        self.function = function
    
    def click(self, pos):
        exec(self.function)
        return
        
class Slider(Widget):
    def __init__(self, parent, length=100, value=1.0, function=None):
        self.length = length
        # function will be called each time the slider's value changes
        self.function = function
        self.value = value
        self.slide = pygame.Surface((length, 10)).convert_alpha()
        self.slide.fill((0, 0, 0, 0))
        pygame.draw.line(self.slide, (200, 200, 200), (0, 5), (length, 5))
        self.slider = pygame.Surface((10, 10)).convert_alpha()
        self.slider.fill((200, 200, 200))
        self.slider_rect = pygame.Rect(0, 0, 10, 10)
        Widget.__init__(self, parent, self.slide.get_rect())
        self.redraw()
        
    def drag(self, pos):
        x, y = pos
        value = float(x - 5) / (self.length - 10)
        if value < 0:
            value = 0.0
        elif value > 1:
            value = 1.0
        self.value = value
        if self.function:
            self.function(self.value)
        self.redraw()
    
    def hover(self, pos):
        if self.slider_rect.collidepoint(pos):
            self.slider.fill((255, 255, 255))
            self.redraw()
        else:
            self.unhover()
    
    def unhover(self):
        self.slider.fill((200, 200, 200))
        self.redraw()
    
    def redraw(self):
        self.slider_rect.left = self.value * (self.length - 10)
        self.surface = self.slide.copy()
        self.surface.blit(self.slider, self.slider_rect.topleft)
        self.parent.redraw()
        
        
class VertSlider(Widget):
    def __init__(self, parent, height=100, value=1.0, function=None):
        #print "VertSlider"
        self.height = height
        # function will be called each time the slider's value changes
        self.function = function
        self.value = value
        self.slide = pygame.Surface((10, self.height)).convert_alpha()
        self.slide.fill((0, 0, 0, 0))
        pygame.draw.line(self.slide, (200, 200, 200), (5, 0), (5, height))
        self.slider = pygame.Surface((10, 10)).convert_alpha()
        self.slider.fill((200, 200, 200))
        self.slider_rect = pygame.Rect(0, 0, 10, 10)
        Widget.__init__(self, parent, self.slide.get_rect())
        self.render()
        
    def drag(self, pos):
        x, y = pos
        value = float(y - 5) / (self.height - 10)
        if value < 0:
            value = 0.0
        elif value > 1:
            value = 1.0
        self.value = value
        if self.function:
            self.function(self.value)
        self.render()
    
    def hover(self, pos):
        if self.slider_rect.collidepoint(pos):
            self.slider.fill((255, 255, 255))
            self.render()
        else:
            self.unhover()
    
    def unhover(self):
        self.slider.fill((200, 200, 200))
        self.render()
    
    def render(self):
        self.slider_rect.top = self.value * (self.height - 10)
        self.surface = self.slide.copy()
        self.surface.blit(self.slider, self.slider_rect.topleft)
    
    def draw(self):
        return self.surface, self.rect        



class VerticalScrollable(Vertical):
    def __init__(self, parent, width=0, height=100):
        Vertical.__init__(self, parent)
        self.width = width
        self.rect.size = (width, height)
        self.value = 1.0
        self.redraw()
        
    def redraw(self, *args):
        if len(self) > 0:
            surface, rect = self[0].draw()
            # Calculate offset
            self.offset = (rect.height - self.rect.height) * self.value
            if self.offset < 0:
                self.offset = 0
            
            # Reinitialise self.rect width
            if (self.width > 0):
                self.rect.width = self.width
            else:
                self.rect.width = rect.width
            # Create a new surface for the container
            self.surface = pygame.Surface(self.rect.size).convert_alpha()
            self.surface.fill(self.background)
            # Blit child widget with y offset
            self.surface.blit(surface, (0, 0), pygame.Rect(0, self.offset, rect.width, self.rect.height))
        else:
            self.surface = pygame.Surface(self.rect.size).convert_alpha()
            
        self.parent.redraw()
        
    def over(self, pos):
        x, y = pos
        x -= self.rect.x
        y -= self.rect.y
        y += self.offset
        pos = x, y
        for widget in self:
            over, local_pos = widget.over(pos)
            if over is not None:
                return over, local_pos
        return None, None
    
    def scroll(self, value):
        self.value = value
        self.redraw()


class TextChat(Horizontal):
    #chat history table and scroll vertslider
    def __init__(self, parent, size):
        Horizontal.__init__(self, parent, background=(0, 0, 0, 0), align=CENTRE, padding=2)
        VerticalScrollable(self, 0, size[1])
        chat = Table(self[0], 2)
        client.chat = chat
        VertSlider(self, size[1], 1, self[0].scroll)
        chat.redraw()

        
class ChatInputReturn(TextInput):
    def __init__(self, parent, text="", minwidth=0):
        TextInput.__init__(self, parent, text, minwidth)
        self.history = []
        self.parent = parent
        self.server = client.server
    
    def enter(self):
        # Don't send empty messages
        if len(self.text) > 0:
            self.server.send(ToServer.Say(self.text))
            self.history.append(self.text)
            self.text = ""
            self.redraw()
        #self.parent[0].refresh()
        
    def esc(self):
        self.unfocus()

class TextChatContainer(Vertical):
    #textchat and input bar
    def __init__(self, parent, size):
        Vertical.__init__(self, parent, background=(0, 0, 0, 180), align=RIGHT, padding=2)
        chatsize = (size[0]-1, size[1])
        TextChat(self, chatsize)
        ChatInputReturn(self, "", size[0])
        self.name="-TCC-"







        
class Spacer(Widget):
    def __init__(self, parent, size, background=(0, 0, 0, 0)):
        Widget.__init__(self, parent, pygame.Rect((0, 0), size))
        self.surface = pygame.Surface(size).convert_alpha()
        self.surface.fill(background)
        

class Checkbox(Widget):
    def __init__(self, parent, value = False):
        self.surface = client.graphics[os.path.join("Data", "UI", "checkbox.png")]
        self.rect = pygame.Rect((0, 0), (12,12))
        Widget.__init__(self, parent, self.rect)
        self.surfaces = []
        self.surfaces.append(self.surface.subsurface(self.rect))
        self.rect.move_ip(12,0)
        self.surfaces.append(self.surface.subsurface(self.rect))

        self.value = value
    
    def click(self, pos):
        self.value = not self.value
            
    def draw(self):
        return self.surfaces[int(self.value)], self.rect


class HoverButton(Widget):
    def __init__(self, parent, image, drawclicked=False):
        if drawclicked:
            self.surface = client.button_graphics.__load__(image, 4)
        else:
            self.surface = client.button_graphics.__load__(image)
        self.rect = self.surface[0][0].get_rect()
        self.currentsurface = 0
        self.drawclicked = drawclicked
        Widget.__init__(self, parent, self.rect)
        self.parent.redraw()
        
    def hover(self, pos):
        if self.currentsurface != 3:
            self.currentsurface = 1
            self.parent.redraw()
    
    def unhover(self):
        if self.currentsurface != 3:
            self.currentsurface = 0
            self.parent.redraw()
        
    def click(self, pos):
        self.clickfunction()
        self.currentsurface = 2
        self.parent.redraw()
        
    def clicked(self, pos):
        self.hover(pos)
        if self.drawclicked:
            self.currentsurface = 3
            self.parent.redraw()
    
    def unclick(self):
        self.currentsurface = 0
        self.parent.redraw()
        
    def dragged(self, pos):
        self.hover(pos)
        
    def draw(self):
        return self.surface[self.currentsurface][0], self.rect
        
        

class Refresh(Widget):
    def __init__(self, parent, torefresh):
        self.surface = client.graphics[os.path.join("Data", "UI", "refresh.png")]
        self.rect = pygame.Rect((0, 0), self.surface.get_size())
        Widget.__init__(self, parent, self.rect)
        self.torefresh=torefresh
        self.parent.redraw()
    
    def click(self, pos):
        self.torefresh.refresh()


        
class BePlayerOne(HoverButton):
    def __init__(self, parent):
         HoverButton.__init__(self, parent, "p1button.png", True)
         self.grouping = "choice"
         self.parent = parent
    def clickfunction(self):
        print "I want to be player 1!"
        client.server.send(ToServer.ChoosePlayer(1))
        for thing in self.parent:
            if thing.grouping=="choice":
                thing.unclick()
        
class BePlayerTwo(HoverButton):
    def __init__(self, parent):
         HoverButton.__init__(self, parent, "p2button.png", True)
         self.grouping = "choice"
         self.parent = parent
    def clickfunction(self):
        print "I want to be player 2!"
        client.server.send(ToServer.ChoosePlayer(2))
        for thing in self.parent:
            if thing.grouping=="choice":
                thing.unclick()
        
class BePlayerNone(HoverButton):
    def __init__(self, parent):
         HoverButton.__init__(self, parent, "p0button.png", True)
         self.grouping = "choice"
         self.parent = parent
    def clickfunction(self):
        print "I don't mind which player I am!"
        client.server.send(ToServer.ChoosePlayer(0))
        for thing in self.parent:
            if thing.grouping=="choice":
                thing.unclick()

class WinLose(Widget):
    def __init__(self, parent, winlose):
        client.sounds.music('Fanfare.ogg', 0)
        self.parent = parent
        if winlose == "win":
            self.surface = client.graphics[os.path.join("Data", "UI", "YouWin.png")]
        else:
            self.surface = client.graphics[os.path.join("Data", "UI", "YouLose.png")]
        topleft = (Options.SCREEN_WIDTH/2 - self.surface.get_size()[0]/2, Options.SCREEN_HEIGHT/2 - self.surface.get_size()[1]/2)
        self.rect = pygame.Rect(topleft, self.surface.get_size())
        Widget.__init__(self, parent, self.rect)
    def click(self,pos):
        client.server.send(ToServer.ChoosePlayer(-1))
        self.parent.remove(self)